iOS 组件化实践《二》基于现有项目拆分组件化实践

0x01 前言

第一篇介绍中已经介绍了如果发布一个私有pod,下面将会介绍如何基于一个现有的项目拆分成组件化形式的项目。
假设我们现有的项目是有一个AViewController,还有一个BViewController,目前的功能是点击rootViewController页面的按钮会push到AviewController,然后点击AviewController的一个按钮会跳转到BViewController。这里是未拆分的demo

0x02 拆分组件

现在我们要拆分AViewController和BViewController,将他们拆分成两个组件,并且按照Target-Action的形式提供对外的接口,通过中间件去调用Target对应的Action,在最上层是中间件的分类,给调用方提供具体且便利的方法和接口。

0x02.1 分析如何拆分AViewController

  1. 首先AViewController,现在AViewController需要外部传一个NSString类型的参数进去,然后返回一个AViewController的对象。这个时候应该创建一个Target_AViewController的类,然后对外一个Action_fectchAViewController:的方法,这个方法需要接收一个字典类型的参数,到时我们会从这个字典中取出key为navTitle的值来作为AViewController的参数。以下是该方法的实现:

    1
    2
    3
    4
    5
    6
       - (UIViewController*)Action_fetchAViewController:(NSDictionary*)params{
    AViewController *aViewControlelr = [[AViewController alloc]init];
    NSString *title = params[@"navTitle"];
    aViewControlelr.navTitle = title;
    return aViewControlelr;
    }
  1. 然后我们要创建CTMediator的分类CTMediator+ModuleAActions,这个分类方法对外提供了一个fetchAViewController的方法,到时调用方调用此方法即可获取到一个AViewController的实例,而不用自己去初始化AViewController。

  2. 那么上面那个分类具体做了什么操作呢,该方法会传一个“AViewController”和“fetchAViewController”来表明需要调用Target_AViewController的Action_fetchAViewller方法,也就是我们刚刚提供的Target_Action。本来调用方是不用通过类别来调用组件Target_Action的,因为可以通过CTMediator的perform Target: action: params:来直接调用。但是那样非常不友好以及不统一。
    加上了该分类之后,可以做更多的容错处理以及有一个容易的入口。

  3. 所以在后面做成pod的时候,category是一个pod,它只依赖于CTMediator,调用方法是通过CTMediator通过runtime来找到Target_Action来调用。所以另外一个pod就是AViewController+Target_AViewController。

    1
    2
    3
    4
    5
    6
    7
    8
       - (UIViewController*)fetchAViewController{
    UIViewController *vc = [self performTarget:@"AViewController" action:@"fetchAViewController" params:@{@"navTitle":@"This is A ViewController"} shouldCacheTarget:NO];
    if ([vc isKindOfClass:[UIViewController class]]) {
    return vc;
    }else{
    return [[UIViewController alloc]init];
    }
    }

以上提到的pod:
CategoryAViewController
AviewControllerPod
CTMediactor

0x02.2 创建BViewController的category和Target_Action的pod

1、为什么需要先创建BViewController组件,因为AViewController会依赖于BViewController,所以我们先创建BViewController组件。

2、首先我们先创建BViewController私有pod,按照我的组件化实践《一》创建好BViewController的pod,这个pod不用依赖于任何其他组件,所以应该没啥困难。

3、接下来我们需要创建BViewControllerCategory,这里有一点要注意的是在podspec文件的最下面需要加入s.dependency 'CTMediator',因为里面需要用到CTMediator的perform方法去调用到对应的组件。因为CTMediator已经发布到cocoapods所以可以直接用,如果这里的依赖是你自己改动之后的私有Mediator,记得在pod spec lint 的时候要加上--sources=yourgithub/spec,master将源改成你自己的私有索引库的地址,不然默认是从公有索引库里面找不到对应的Mediator。待会在AViewController调用B的时候会用到这个命令。

0x02.3 创建AViewController的category和Target_Action的pod

1、方法同上,首先创建AViewController的Category组件,这里只需要注意在podspec文件的最下面需要加入s.dependency 'CTMediator'
2、 然后我们创建AViewController私有pod,由于在AViewController里面我们有个按钮点击之后会push到BViewController,那必然要引入BViewController,但是在组件中不会直接引入BViewController,我们通过引入BViewControllerCategory来让程序编译的时候不要报错就行。podspec文件要加入s.dependency 'BCategory'

3、 验证,因为第二部中我们的私有工程依赖了另外一个私有工程,这个时候的pod spec lintpod repo push命令都要加上 --source说明

注意:组件间的调用依然是利用Target对应的分类来调用。记住分类是统一的入口,组件里面的代码不会依赖于另外一个组件,而是依赖于对应的分类组件或者CTMediator,然后所有依赖的pod都要写到主工程的podfile里面

0x03 引用组件

我们将AViewController和BViewController拆分后,我们创建一个demo来测试一下刚刚拆分的组件。创建一个OC项目,在项目的跟目录下执行pod init,然后在podfile文件里面写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
source 'https://github.com/codemonkeybulucck/LMSpec'
source 'https://github.com/CocoaPods/Specs.git'

target 'moduleTest' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
pod 'ACategory'
pod 'AViewController','~>0.1.1'
pod 'BViewController'
# Pods for moduleTest

end

完成后执行 pod install,成功之后再#import "CTMediator+ModuleAActions.h" 然后调用方式就变成了
UIViewController *vc = [[CTMediator sharedInstance] fetchAViewController];

0x04 总结

组件化最核心的思想就是解耦,将庞大的项目抽丝剥茧,分离成互相独立的一个一个模块,每个模块都是可复用的。上述的方案其实已经做到了解耦,调用者只需要用到中间件的分类的方法接口而不需要引入实际的组件的类。
但是在分类里面有一个弊端就是:动态调用方法的时候需要硬编码也就是,这样容错率就很低,不过幸好这个东西是在分类中,在开发的过程中就可以发现的问题。
另外一个方面也不是所有的项目都适合用组件化,只有当项目规模扩大到一定的程度,并且人员众多的情况下用组件化的便捷性才体现出来。所以很多时候我们讨论架构或者讨论一种新的技术,并不是它就是最好的,没有什么东西是最好的,只有最合适的,因地制宜。

-------评论系统采用disqus,如果看不到需要翻墙-------------